﻿using System;
using System.Collections.Generic;
using System.Linq;
using PureMVC.Patterns;
using GameEngine.Model.Data;

namespace GameEngine.Model
{
    public class EngineDataProxy<ItemVOId> : Proxy
        where ItemVOId : IComparable<ItemVOId>
    {
        public const string LEVELENDED_NOTIFICATION = "LevelEnded_EngineDataProxy";
        public const string MYNAME = "engineDataProxy";
        public IDictionary<IShapeTypeVOId, IShapeTypeVO> ShapeTypes { get; set; }
        public IDictionary<string, Type> ThingTypeTypes { get; set; }
         public static IDictionary<Guid, WorldAction> WorldActions = new Dictionary<Guid, WorldAction>();
        private bool EnginePaused { get; set; }

        private IGameDataProxy<ItemVOId> game;
        public IGameDataProxy<ItemVOId> Game
        {
            get
            {
                return game;
            }
            set
            {
                game = value;
                EnginePaused = game==null;
            }
        }

        protected EngineDataProxy()
            : base(MYNAME, null)
        {
            ShapeTypes = new Dictionary<IShapeTypeVOId, IShapeTypeVO>();
            ThingTypeTypes = new Dictionary<string, Type>();
            EnginePaused = true;
        }

        protected static EngineDataProxy<ItemVOId> instance;
        public static EngineDataProxy<ItemVOId> Instance
        {
            get
            {
                if (instance == null)
                    instance = new EngineDataProxy<ItemVOId>();
                return instance;
            }
        }

        static double lastTime = 0;
        virtual public void Tick()
        {
            if (!EnginePaused)
            {
                if (!Game.CurrentLevel.Paused)
                {
                    
                    double now = TimeSpan.FromTicks(DateTime.Now.Ticks).TotalMilliseconds;
                    if (now != lastTime)//correr só ao milisegundo
                    {
                        //lock (Game.CurrentLevel.Worlds)
                        //{
                        foreach (IWorldDataProxy<ItemVOId> world in Game.CurrentLevel.Worlds.Values)
                        {
                            if (!world.Pause)
                            {
                                //world.Map.Tick();
                                //copia-se a lista pq algum elemento pode adicionar/remover outros elementos
                                IThingDataProxy<ItemVOId>[] tempList = null;
                                foreach (WorldAction worldAction in WorldActions.Values)
                                {
                                    //while (worldAction.TimeInterval < now - worldAction.LastTime)
                                    if ((worldAction.TimeInterval < (now - worldAction.LastTime)) || worldAction.LastTime == 0)
                                    {
                                        if (tempList == null)
                                            tempList = world.Items.Values.ToArray();
                                        foreach (IThingDataProxy<ItemVOId> item in tempList)
                                        {
                                            //if (item.GetType().GetInterface(worldAction.InterfaceName, false) != null)
                                            if(!item.MovingWithMouse)//TODO isto não devisa estar dentro da action?
                                            if (worldAction.ClassType.IsAssignableFrom(item.GetType()))
                                                worldAction.Method.Invoke(
                                                    this,
                                                    world,
                                                    game.RetrieveThingTypeDataProxy(game.ThingTypes[item.TypeId]),
                                                    item);
                                        }
                                        //worldAction.LastTime += worldAction.TimeInterval;
                                        worldAction.LastTime = now;
                                    }
                                }
                            }
                        }
                        //}

                        //TODO ver isto só de x em x tempo
                        if (Game.CurrentLevel.HasEnded())
                        {//TODO haswan haslost
                            EnginePaused = true;
                            ILevelDataProxy<ItemVOId> nextLevel = Game.NextLevel;
                            if (nextLevel != null)
                            {
                                SendNotification(ApplicationFacade.CREATELEVEL_COMMAND, nextLevel);
                                EnginePaused = false;
                            }
                        }
                        lastTime = now;
                    }
                }
            }

        }
        /*
        virtual public void AddMap(IMapDataProxy map) {
            Worlds.Add(map.Name, new World() { Map = map });
        }

        virtual public void RemoveMap(string mapName) {
            Worlds.Remove(mapName);
        }

        virtual public void AddItemToMap(IThingDataProxy item, string mapName)
        {
            Worlds[mapName].Items.Add(item);
            //TileOcupation.Add(Map.GetTileFromPosition(item.Position), item);
         }
        
        virtual public void RemoveItemFromMap(IThingDataProxy item, string mapName)
        {
            Worlds[mapName].Items.Remove(item);
            //TileOcupation.Remove(Map.GetTileFromPosition(item.Position));
        }*/

        public void AddWorldAction(WorldAction action)
        {
            WorldActions.Add(
                action.ClassType.GUID,
                action);
        }

        public string Serialize() {
            return "";
        }

        public class WorldAction
        {

            public delegate void WorldActionMethod(EngineDataProxy<ItemVOId> engine, IWorldDataProxy<ItemVOId> map, IThingTypeDataProxy<ItemVOId> itemType, IThingDataProxy<ItemVOId> item);

            //public string InterfaceName { get; set; }
            public Type ClassType { get; set; }

            public WorldActionMethod Method { get; set; }

            public long TimeInterval { get; set; }

            public double LastTime { get; set; }

            public WorldAction(Type classType, WorldActionMethod method, long timeInterval)
            {
                ClassType = classType;
                Method = method;
                TimeInterval = timeInterval;
            }
        }
    }
}
